/*
* Description: 取CPU使用率和频率数据
*/
#ifndef SYS_CPU_CALC_H
#define SYS_CPU_CALC_H
#include <mutex>
#include <memory>
#include <functional>
#include <map>
#include <vector>
#include <deque>
#include <string>
#include <unordered_set>
#include <winsock2.h>
#include <windows.h>
#include <bcrypt.h>

const int HUNDRED_CPU_GHZ = 100;
const int HUNDRED_CPU_USAGE = 100;
const LONG LENGTH_IS_NOT_MATCH = 0xC0000004L;
const int INVALID_STATE_COUNT = 2;
const int DEFAULT_PERFORMANCE_HIT_COUNT = 2;
const int STANDARD_FREQUENCY = 1000;
const int CPU_USAGE_HALF = 2;
const ULONG SYSTEM_PROCESSO_RPERFORMANCE_DISTRIBUTION = 0x4000;
const ULONG ENUM_SYSTEM_INFO_ADD_BUFF_SIZE = 1024;
const int PROCESS_CPU_MAP_DEQUE_LEN = 3;
const int DEQUE_POS_1 = 1;
const int DEQUE_POS_2 = 2;
const int DEQUE_POS_3 = 3;
const int SYSTEM_ILD_PROCESS_ID = 4;
const int FRESH_PROCESS_CPU_DATA_WAIT_TIME = 200;
const int MAX_LOOP_TIMES = 500;
const double THOUSAND_CPU_GHZ = 1000.0;

enum PerfSystemInfomationClass {
    SYSTEM_BASIC_INFORMATION_PERF = 0,
    SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_PERF = 8,
    SYSTEM_PROCESSOR_PERFORMANCE_DISTRIBUTION_PERF = 100
};

#pragma comment(lib, "ntdll.lib")
extern "C" {
    NTSTATUS __stdcall NtPowerInformation(
        _In_ POWER_INFORMATION_LEVEL informationLevel,
        _In_reads_bytes_opt_(InputBufferLength) PVOID inputBuffer,
        _In_ ULONG inputBufferLength,
        _Out_writes_bytes_opt_(OutputBufferLength) PVOID outputBuffer,
        _In_ ULONG outputBufferLength
    );
}

struct PerfProcessorPowerInfo {
    ULONG number;
    ULONG maxMhz;
    ULONG currentMhz;
    ULONG mhzLimit;
    ULONG maxIdleState;
    ULONG currentIdleState;
};

struct PerfSystemBasicInformation {
    ULONG reserved;
    ULONG timerResolution;
    ULONG pageSize;
    ULONG numberOfPhysicalPages;
    ULONG lowestPhysicalPageNumber;
    ULONG highestPhysicalPageNumber;
    ULONG allocationGranularity;
    ULONG_PTR minimumUserModeAddress;
    ULONG_PTR maximumUserModeAddress;
    ULONG_PTR activeProcessorsAffinityMask;
    CCHAR numberOfProcessors;
};

struct SystemProcessorPerformanceDistribution {
    ULONG processorCount;
    ULONG offsets[1];
};

struct PhUint64Delta {
    ULONG64 value;
    ULONG64 delta;
};

struct PerfClientID {
    HANDLE uniqueProcess;
    HANDLE uniqueThread;
};

enum PerfThreadState {
    INITIALIZED,
    READY,
    RUNNING,
    STANDBY,
    TERMINATED,
    WAITING,
    TRANSITION,
    DEFERRED_READY,
    GATE_WAIT_OBSOLETE,
    WAITING_FOR_PROCESS_IN_SWAP,
    MAXIMUMTHREADSTATE
};

enum PerfWaitReason {
    EXECUTIVE,
    FREE_PAGE,
    PAGE_IN,
    POOL_ALLOCATION,
    DELAY_EXECUTION,
    SUSPENDED,
    USER_REQUEST
};

struct PerfSystemThreadInfo {
    LARGE_INTEGER kernelTime;
    LARGE_INTEGER userTime;
    LARGE_INTEGER createTime;
    ULONG waitTime;
    PVOID startAddress;
    PerfClientID clientId;
    LONG priority;
    LONG basePriority;
    ULONG contextSwitches;
    PerfThreadState threadState;
    PerfWaitReason waitReason;
};

struct PerfUniString {
    USHORT length;
    USHORT maximumLength;
    _Field_size_bytes_part_(maximumLength, length) PWCH buffer;
};

struct PerfSystemProcessInfo {
    ULONG nextEntryOffset;
    ULONG numberOfThreads;
    LARGE_INTEGER workingSetPrivateSize; // since VISTA
    ULONG hardFaultCount; // since WIN7
    ULONG numberOfThreadsHighWatermark; // since WIN7
    ULONGLONG cycleTime; // since WIN7
    LARGE_INTEGER createTime;
    LARGE_INTEGER userTime;
    LARGE_INTEGER kernelTime;
    PerfUniString imageName;
    LONG basePriority;
    HANDLE uniqueProcessId;
    HANDLE inheritedFromUniqueProcessId;
    ULONG handleCount;
    ULONG sessionId;
    ULONG_PTR uniqueProcessKey; // since VISTA (requires SystemExtendedProcessInformation)
    SIZE_T peakVirtualSize;
    SIZE_T virtualSize;
    ULONG pageFaultCount;
    SIZE_T peakWorkingSetSize;
    SIZE_T workingSetSize;
    SIZE_T quotaPeakPagedPoolUsage;
    SIZE_T quotaPagedPoolUsage;
    SIZE_T quotaPeakNonPagedPoolUsage;
    SIZE_T quotaNonPagedPoolUsage;
    SIZE_T pagefileUsage;
    SIZE_T peakPagefileUsage;
    SIZE_T privatePageCount;
    LARGE_INTEGER readOperationCount;
    LARGE_INTEGER writeOperationCount;
    LARGE_INTEGER otherOperationCount;
    LARGE_INTEGER readTransferCount;
    LARGE_INTEGER writeTransferCount;
    LARGE_INTEGER otherTransferCount;
    PerfSystemThreadInfo threads[1];
};

struct PerfSystemProcessorInfo {
    LARGE_INTEGER idleTime;
    LARGE_INTEGER kernelTime;
    LARGE_INTEGER userTime;
    LARGE_INTEGER dpcTime;
    LARGE_INTEGER interruptTime;
    ULONG interruptCount;
};

struct SysProcessorPerfHitCnt {
    LARGE_INTEGER hits; // ULONG in WIN8
    UCHAR percentFrequency;
};

struct SysProcessorPerfStatDist {
    ULONG processorNumber;
    ULONG stateCount;
    SysProcessorPerfHitCnt states[1];
};

struct SystemProcessItem {
    DWORD processId = 0;
    std::string processName = "";
    std::string fullProcessName = "";
    PhUint64Delta cpuKernelDelta = {0, 0};
    PhUint64Delta cpuUserDelta = {0, 0};
    float cpuUsage = 0.0f;
    float cpuKernelUsage = 0.0f;
    float cpuUserUsage = 0.0f;
    PhUint64Delta writeIoDelta = { 0, 0 };
    PhUint64Delta readIoDelta = { 0, 0 };
    ULONGLONG diskReadPerSec = 0;
    ULONGLONG diskWritePerSec = 0;
    PhUint64Delta ioDelta = { 0, 0 };
    std::deque<ULONG64> ioTotalDeque = {}; // 存储3组IO数据
    ULONG64 ioToTal = 0L; // 累计6s进程IO
    ULONG64 mem = 0;
    ULONG64 vmem = 0;
};

// 单个进程CPU使用率
struct SysProcessCpuUsage {
    DWORD processId = 0;
    std::string processName = "";
    float cpuUsage = 0.0f;
};

class SysCpuCalc {
public:
    static SysCpuCalc& GetInstance();
    void PhpUpdateCpuInformation();
    void DttPhpUpdateCpuInformation();
    bool Init();
    float GetSysCpuUsage();
    void CollectProcLoad();
    // 采集进程CPU使用率信息
    void CollectProcCpuLoad();
    std::map<DWORD, SystemProcessItem> GetProcessInfos();
    std::map<DWORD, SystemProcessItem> GetFilterProcessInfos();
    bool FilterFileCopyAndPasteActions();
    bool FilterCompressDecompressActions();
    void GetProcessesPid(std::unordered_set<std::string> whiteList, std::unordered_set<DWORD>& pidList);
    std::string LpwstrToString(LPWSTR lpwstr);
    bool FilterAPPResourceActions();
    double GetCpuGhz();
private:
    bool inited = false;
    ULONG64 sysTotalTime = 0;
    PerfSystemProcessorInfo* phCpuInformation = nullptr;
    PerfSystemProcessorInfo phCpuTotals = {0};
    PerfSystemBasicInformation phSystemBasicInformation = {0};
    PhUint64Delta phCpuKernelDelta = {0, 0};
    PhUint64Delta phCpuUserDelta = {0, 0};
    PhUint64Delta phCpuIdleDelta = {0, 0};
    FLOAT phCpuKernelUsage = 0.0f;
    FLOAT phCpuUserUsage = 0.0f;
    int numberOfProcessors = 0;
    std::map<DWORD, SystemProcessItem> mapProcessInfo = {};
    std::map<DWORD, SystemProcessItem> filterMapProcessInfo = {};
    SYSTEMTIME lastTimePoint = { 0 };
    float sysCpuUsage = 0.0f;
    // 另一套进程CPU使用率相关采集参数（防止和之前的2秒定时器冲突）
    PerfSystemProcessorInfo* dttPhCpuInformation = nullptr;
    PerfSystemProcessorInfo dttPhCpuTotals = { 0 };
    PhUint64Delta dttPhCpuKernelDelta = { 0, 0 };
    PhUint64Delta dttPhCpuUserDelta = { 0, 0 };
    PhUint64Delta dttPhCpuIdleDelta = { 0, 0 };
    std::mutex mapMtx{};
    std::map<DWORD, SystemProcessItem> mapDttProcessInfo = {};
    std::mutex dttMapMtx{};
    ULONG64 dttSysTotalTime = 0;
    std::unordered_set<std::string> copyPasteWhiteList = {
        "vmgtsharemgr.exe",
        "explorer.exe"
    };
    std::unordered_set<std::string> compressWhiteList = {
        "Bandizip.exe",
        "WinRAR.exe",
        "7zG.exe",
        "explorer.exe"
    };
    std::unordered_set<std::string> appWhiteList = {"acad.exe"};
    const std::string SevenCompressName = "7zG.exe";
    float copyFileMiniCpuUsage = 0.03f;
    ULONGLONG fileReadDisk = 5000;
    ULONGLONG fileWriteDisk = 5000;
    float compressFileMiniCpuUsage = 0.04f;
    float compressFile7ZMiniCpuUsage = 0.1f;
    ULONGLONG compressFileReadDisk = 2000;
    ULONGLONG compressFileWriteDisk = 2000;
    std::unique_ptr<char[]> autoPtrPrePerformanceDistribution = nullptr;
private:
    SysCpuCalc(const SysCpuCalc& spm) = delete;
    SysCpuCalc& operator=(const SysCpuCalc& spm) = delete;
    SysCpuCalc(const SysCpuCalc&& spm) = delete;
    SysCpuCalc& operator=(const SysCpuCalc&& spm) = delete;
    SysCpuCalc();
    ~SysCpuCalc();

    bool AquireToken(HANDLE processHandle, LPCTSTR privilegeName);
    std::unique_ptr<char[]> QueryProcessorPerformanceDistribution();
    std::unique_ptr<char[]> GetProcessInfo();
    void GetProcessMappingInfo(std::map<DWORD, SystemProcessItem>& mapProcessInfo,
        bool isDttSample = false);
    void ClearExitProcess(const std::unordered_set<DWORD>& setProcssId,
        std::map<DWORD, SystemProcessItem>& mapProcInfo);
    void UpdateProcInfo(SystemProcessItem& info, PerfSystemProcessInfo& proc, double timeDiff,
        bool isDttSample = false);
    void InitProcInfo(SystemProcessItem& info, PerfSystemProcessInfo& proc, DWORD processId);
    void FilterEffectiveProcesses();
    void CalcIoTotal(SystemProcessItem& procInfo, double timeDiff);
    void ProcessProcInfo(PerfSystemProcessInfo &proc, std::unordered_set<DWORD> &setProcssId,
        std::map<DWORD, SystemProcessItem> &mapProcInfo, double timeDiff, bool isDttSample);
    ULARGE_INTEGER GetElapsedTime();
    
    std::string GetFullNameByProcessId(DWORD pid);
    std::string GetProcNameFromPath(const std::string &fullPath);
    std::string WStringToString(const std::wstring &wString);
    void PhUpdateDelta(PhUint64Delta &dltmgr, ULONG64 newValue);
    bool NtSuccess(const NTSTATUS status);
};
#endif // !SYS_CPU_CALC_H